哈摟~大家好我是阿華,今天來跟大家分享 TypeScript 基礎類型,
TypeScript 提供了多種類型,用於宣告變數、函數參數、函數傳回值等,以協助開發者更準確地定義和使用資料。
以下是 TypeScript 中常見的一些類型介紹 :
基本原始(The primitives)型態:
const strExample: string = "str ...";
const numExample: number = 100;
const boolExample: boolean = true; // or false
const nullExample: null = null;
const undefinedExample: undefined = undefined;
const symbolExample: symbol = Symbol("name");
const bigintExample: bigint = 10n;
宣告陣列(Array)類型
若是要宣告陣列類型如字串陣列、數字陣列、物件陣列,可以透過以下方法宣告變數的類型:
const strArray: string[] = ["str ...", "str 1 ..."];
const numArray: number[] = [100, 101, 102];
const boolArray: boolean[] = [true, false, false];
const mixedArray: (string | null)[] = ["str", null, "str1"]; // '|' 表示 '或'
如果想要宣告一個固定長度和類型的陣列(tuple),可以透過以下方法宣告變數的類型:
const person: [string, number] = ["John", 30];
宣告物件(Object)類型
若是要宣告物件類型(Object),可以透過以下 Record 型別搭配指定泛類型的方法宣告類型:
const obj: Record<string, number> = { keyName: 100 };
上面例子中 Record 第一個可以傳入的泛類型我們指定為 string,代表我們物件的 key 的類型是 string,
第二個可以傳入的泛類型我們指定為 number,代表我們物件的 value 是 number,
Interface
又如果我們想指定物件內的多個屬性的個別類型,
我們可以方便地透過 interface 關鍵字去宣告:
interface MyObj {
props1: string;
props2: number;
props3: (string | null)[];
}
const obj: MyObj = {
props1: "string...",
props2: 101,
props3: [null],
};
宣告函數(Function)類型
若是要宣告函數(Function)類型,可以透過以下方法宣告類型:
// 一般函數
function add(x: number, y: number): number {
return x + y;
}
// 或以箭頭函數宣告類型
const add: (x: number, y: number) => number = (x, y) => x + y;
// 位置格式搭配中文解說
// 一般函數
function add(x: x的類型, y: y的類型): 函數返回的類型 {
return x + y;
}
// 箭頭函數 add 的類型
const add: 整個箭頭函數的類型 = (x, y) => x + y;
// 整個箭頭函數的類型 : (x: number, y: number) => number
上面兩個例子中,都表示 add 這個函數,接受 x 跟 y 兩個參數類型都是 number,並回傳 number 類型
如果在使用 add 函數時,傳入兩個參數類型不是 number 時,就會提示錯誤
宣告枚舉(Enum)類型
若是要宣告枚舉(Enum)類型,可以透過以下方法宣告 :
enum:表示一組命名的常數值。
enum Color {
Red,
Green,
Blue,
}
let favoriteColor: Color = Color.Blue;
上面例子在編譯成 js 後,相當於有一個 Color 物件,所以可以以 Color.Blue 的方式使用
// enum Color {
// Red,
// Green,
// Blue,
// }
// 編譯後的結果 (v5.2.2, target:ES2017)
var Color;
(function (Color) {
Color[(Color["Red"] = 0)] = "Red";
Color[(Color["Green"] = 1)] = "Green";
Color[(Color["Blue"] = 2)] = "Blue";
})(Color || (Color = {}));
// console.log(Color)
// {
// 0: "Red",
// 1: "Green",
// 2: "Blue",
// Blue: 2,
// Green: 1,
// Red: 0,
// };
宣告 any 類型
在某些情況我們不希望特定值導致類型檢查錯誤時,我們可以使用 any 類型:
const value: any = 42; // 不管放什麼類型的值都不會觸發錯誤
直得注意的是,我們需要慎用 any,因為它會喪失類型檢查的好處。
若不想喪失類型檢查的好處,可以使用 unknown 來代替,
宣告 unknown 類型
unknown 顧名思義指的是未知的類型,就是你不知道它確切類型。
這通常用於處理來自不可信來源或動態資料的情況,或者當你需要編寫通用的、類型安全的程式碼,
而不知道值的類型是什麼的時候使用。
例如:
let value: unknown = "Hello, TypeScript";
let strLength: number = value.length;
// 因為現在 value 的類型是 unknown,這裡會顯示錯誤 Object is of type 'unknown'.
// 我們應該先斷言他的類型為 string
let strLength: number = (value as string).length;
// Object is of type 'unknown'.
總之使用 unknown 有助於提高程式碼的類型安全性,因為它要求你在使用值之前進行類型檢查或類型斷言,而不是如 any 一樣喪失類型檢查。
我們接著往下介紹幾種常用的符號,
聯合(Union)類型
如果我們想讓變數可以有多種不同類型的值,我們可以使用聯合(Union)類型:
let name: string | null = "John";
// string | null:表示變數可以有是string 或 null
交集(Intersection)類型
如果我們想讓變數'同時'具有多種類型的特性,我們可以使用交集(Intersection)類型:
// Person & Age:表示變數可以同時具有兩種類型的特性。
interface Person {
name: string;
}
interface Age {
age: number;
}
let john: Person & Age = { name: "John", age: 30 };
類型別名(Type Alias)
如果我們需要建立自訂類型別名,用於簡化複雜類型的表示我們可以使用類型別名(Type Alias):
type Point = { x: number; y: number };
let origin: Point = { x: 0, y: 0 };
看到這裡眼尖的大家可能會好奇 type 跟 interface 有什麼不一樣?
節錄自官方說明:
Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.
type 跟 interface 兩者非常像,基本上大多時候可以自由選擇使用,兩者不一樣的關鍵點在 interface 同名字會繼承,type 不會繼承,基本上大多時候可以自由選擇使用,例如:
// Interface
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
const bear = getBear();
bear.name;
bear.honey;
// Type
type Animal = {
name: string;
};
type Bear = Animal & {
honey: boolean;
};
const bear = getBear();
bear.name;
bear.honey;
而兩者不一樣的關鍵點,例如:
// Interface
interface Animal {
title: string;
}
interface Animal {
ts: string;
}
const animal: Animal = {
title: "str",
ts: "str",
}; // Animal繼承了title與ts兩個屬性
// Type
type Animal = {
title: string;
};
type Animal = {
ts: string;
};
// 發出錯誤 Error: Duplicate identifier 'Animal'.
Type Assertions (as)
而有時 TypeScript 無法推斷出理想的類型資訊,所以我們會需要使用 Type Assertions (as)去斷言類型,
例如:
const canvas = document.getElementById("canvas");
// 推斷 canvas 類型為 HTMLElement | null
但我們知道其實他的類型是 HTMLCanvasElement 比較精確,所以我們可以用 as 去斷言他的類型為 HTMLCanvasElement
const myCanvas = document.getElementById("canvas") as HTMLCanvasElement;
unknown
另外這邊補充一種用法,如果 TypeScript 類型資訊差異太大會無法直接用 as 斷言,會需要先斷言成 unknown 在斷言成想要的類型,例如:
interface Color {
detail: string;
}
interface Table {
color: string;
}
const table: Table = {
detail: "string",
} as Table;
// 會顯示錯誤,因為 Table 沒有 detail 屬性
// Conversion of type '{ detail: string; }' to type 'Table' may be a mistake because neither type
//sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
// Property 'color' is missing in type '{ detail: string; }' but required in type 'Table'.
先斷言成 unknown 在斷言成想要的類型可以通過檢查,但這裡舉的是一個極端的例子,也要謹慎使用
const table: Table = {
detail: "string",
} as unknown as Table;
never
再來是 never 類型,它用於表達不正常或不可到達的程式碼路徑,以增強類型安全性。當你看到 never 類型時,通常意味著某些特殊情況或不正常情況的處理。之後我們在聊 Conditional Types 時,會再把 never 拿出來鞭鞭屍
上面這些是 TypeScript 中的一些常見類型。透過使用這些類型,可以增加程式碼的可讀性、可維護性和類型安全性,從而更輕鬆地開發複雜的應用程式。另外根據需求,你可以建立自訂類型和接口,以滿足特定場景的需求。
那這回我們就說到這裡,下回我們聊聊 Narrowing~
參考資料:
https://www.typescriptlang.org/docs/handbook/2/basic-types.html